package cn.trigram.file;

import cn.trigram.logging.Log;
import cn.trigram.logging.LogFactory;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;

/**
 * 文件分分割处理器，以多线程的方式实现
 */
public class FileSegmentGenerator implements Runnable {

  final Log log = LogFactory.getLog(FileSegmentGenerator.class);

  /**
   * 源文件的路径
   */
  private String sourceFilePath;

  /**
   * 当前段分割后文件路径
   */
  private String outFilePath;

  /**
   * 当前段起始位置
   */
  private long start;

  /**
   * 当前段结束位置
   */
  private long end;

  /**
   * 处理时的ByteBuffer的容量,最大10MB
   */
  private int capacity;

  public FileSegmentGenerator(String sourceFilePath, String outFilePath, long startPosition,
      long endPosition, int capacity) {

    this.sourceFilePath = sourceFilePath;
    this.outFilePath = outFilePath;
    this.start = startPosition;
    this.end = endPosition;
    /* 最大10MB */
    this.capacity = Math.min(capacity, 10485760);
  }

  @Override
  public void run() {
//      开始分割文件
    File splitFile = new File(outFilePath);
    try (
        RandomAccessFile randomAccessFile = new RandomAccessFile(sourceFilePath, "r");
        FileChannel channel = randomAccessFile.getChannel();
        FileOutputStream fos = new FileOutputStream(splitFile);
        BufferedOutputStream bos = new BufferedOutputStream(fos)
    ) {
      ByteBuffer channelBuffer = ByteBuffer.allocate(this.capacity);
      byte[] channelBufferDest = new byte[this.capacity];
//        调整到当前段的起始位置
      channel.position(this.start);
      while (true) {
        channelBuffer.clear();
        long readPositionBefore = channel.position();
        int readByteNum = channel.read(channelBuffer);
        if (readByteNum == -1) {
//            读取结束
          break;
        }
        long readPositionAfter = channel.position();
        channelBuffer.flip();
        channelBuffer.get(channelBufferDest, 0, readByteNum);
        if (readPositionAfter > this.end) {
//            超过当前段的结束位置需要调整本次读取的字节数
          readByteNum = new Long(this.end - readPositionBefore).intValue();
        }
        bos.write(channelBufferDest, 0, new Long(readByteNum).intValue());
        if (readPositionAfter > this.end) {
//            超过当前段的结束位置表示分割结束，同时调整channel的位置，为下一段的起始位置
          channel.position(this.end);
          break;
        }
      }
    } catch (Exception e) {
      log.error(String
          .format("分割文件【%s】的第【%d-%d】位置分割失败；【%s】", this.sourceFilePath, this.start, this.end,
              e.getLocalizedMessage()));
    }
  }

}
